<template>
  <div class="editor-frame--renderer">
    <div class="layout-view" @click="handleSelect('screen')">
      <CanvasBox
        :key="screenSize"
        :w="screenWidth"
        :h="screenHeight"
        :scaleSize="scaleSize / 100"
        @updateDragging="updateDragging"
        @keyCodeDelete="keyCodeDelete"
      >
        <div class="screen-content">
          <img
            v-if="curMapId"
            :src="require(`@/assets/map${curMapId}.png`)"
            alt=""
          />
          <div class="screen-decoration">
            <div class="title" @click.stop="handleSelect('screen')">
              {{ screenTitle }}
            </div>
            <div class="grid-stack screen-bi" @click.stop="handleSelect('map')">
              <div
                class="grid-stack-item"
                v-for="item in nodesArr"
                :gs-id="item.id"
                :id="item.id"
                :key="item.id"
                :gs-w="item.w"
                :gs-h="item.h"
                :gs-x="item.x"
                :gs-y="item.y"
              >
                <div class="grid-stack-item-content">
                  <NodeBox
                    :node="item"
                    :isDragging="isDragging"
                    @keyCodeDelete="keyCodeDelete"
                  />
                </div>
              </div>
            </div>
          </div>
        </div>
      </CanvasBox>

      <!-- 缩放 -->
      <div
        class="scale-btn"
        :style="`right: ${selection && selection.type ? 380 : 0}px`"
      >
        <span class="prepend-text" @click.stop="handleSize(false)">-</span>
        <span class="scale-text" @click.stop>{{ scaleSize + '%' }}</span>
        <span class="prepend-text" @click.stop="handleSize(true)">+</span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Renderer',
};
</script>

<script setup>
import { getCurrentInstance } from '@vue/composition-api';
import 'gridstack/dist/gridstack.min.css';
import '@/styles/gridstack-column.scss';
import { GridStack } from 'gridstack';
import { useDragon, useWorkbench, useDocument } from '../hooks';
import NodeBox from '../components/node-box';
import { Node, INode } from '../models/node';
import CanvasBox from './components/canvas-box.vue';
import { SCREEN_SIZE } from '@/designer/constants/index';
import DefaultData from './components/defaultData.json';

const props = defineProps({
  nodes: {
    type: Array,
    default: () => [],
  },
  map: {
    type: Object,
    default: () => ({}),
  },
  screen: {
    type: Object,
    default: () => ({}),
  },
  isOperate: {
    type: Boolean,
    default: false,
  },
});

const { proxy } = getCurrentInstance();
const dragon = useDragon();
const workbench = useWorkbench();
const document = useDocument();
const nodesArr = ref([]);
const selection = document.value.selection;

const assets = computed(() => {
  return workbench.value.assets;
});
const curMapId = computed(() => {
  return props.map.props.mapId;
});
const screenSize = computed(() => {
  return Number(props.map.props.column);
});
const isOperate = computed(() => {
  return props.isOperate;
});
const screenTitle = computed(() => {
  return props.screen.props.title;
});

const isDragging = ref(false);
const handleSelect = (val) => {
  // 无主视觉不弹出属性面板
  if (isDragging.value || (val === 'map' && !curMapId.value)) return;
  document.value.setSelection(props[val]);
};

const column = ref(16); // 默认16列
const screenWidth = ref(1920 - 64); // 64为两侧花边 默认宽度1920
const screenHeight = ref(1080); // 高度固定
const scaleSize = ref(50); // 默认缩小50%

const setMap = () => {
  setTimeout(() => {
    document.value.setSchema(DefaultData);
    document.value.setMapProps('mapId', '2');
    gridInit();
  });
};

const setMapConfirm = async (_id) => {
  // 无主视觉时直接添加
  if (!curMapId.value) {
    setMap();
    return;
  }

  await proxy
    .$confirm('切换主视觉组件时将清空画布，是否切换？', '切换主视觉组件', {
      type: 'warning',
    })
    .then(() => {
      setMap();
    })
    .catch(() => {
      document.value.deleteNode(String(_id));
    });
};

const grid = ref(null);
const gridInit = async () => {
  grid.value = GridStack.init({
    acceptWidgets: true,
    marginTop: 0,
    marginBottom: 16,
    marginLeft: 16,
    marginRight: 0,
    disableResize: true,
    float: true,
    row: 10,
    cellHeight: '108px', // 高度以900px为基准
  });

  nodesArr.value = props.nodes;
  await nextTick();
  grid.value.column(column.value);
  nodesArr.value.forEach((item) => {
    grid.value.makeWidget(`#${item.id}`);
  });

  GridStack.setupDragIn('.sidebar .grid-stack-item', {
    appendTo: 'body',
    helper: 'clone',
  });

  grid.value.on('added', async (event, newWidget) => {
    const newWidgetInfo = newWidget[0] || {};
    const componentName = newWidgetInfo.el.innerText;
    if (!componentName.includes('Map')) return;
    setMapConfirm(newWidgetInfo._id);
  });

  grid.value.on('dragstart', function (event, el) {
    isDragging.value = true;
  });

  // 添加小部件
  grid.value.on('dropped', async (event, previousWidget, newWidget) => {
    const { w, h, x, y, _id } = newWidget;
    // 兼容多标题
    const componentString = newWidget.el.innerText?.split('\n')[0];
    const componentType = componentString?.split('-')[0];
    const componentName = componentString?.split('-')[1];
    const curComponent = Object.values(assets.value.components).find(
      (item) => item.componentName === componentName
    );
    const { props } = curComponent.componentMeta;
    const result = {};
    props.forEach((item) => {
      result[item.name] = item.defaultValue;
    });
    const addNodeData = {
      id: _id + '',
      componentMeta: curComponent.componentMeta,
      props: result,
      componentName: componentName,
      type: componentType,
      x,
      y,
      h,
      w,
    };

    const node = new Node(addNodeData);
    document.value.addNode(node, isOperate.value);
    grid.value.removeWidget(newWidget.el, true, false);
    await nextTick();
    grid.value.makeWidget(`#${_id}`);

    // 拥挤自动布局后更新所有坐标
    const saveItemList = grid.value.save(false);
    saveItemList.forEach((item) => {
      const { id, x, y } = item;
      document.value.mergeNodeInfo(id, { x, y }, isOperate.value);
    });
  });

  // 拖动坐标更新
  grid.value.on('dragstop', function (event, el) {
    setTimeout(() => {
      isDragging.value = false;
    }, 400);
    const { x, y, id } = el.gridstackNode;
    document.value.mergeNodeInfo(id, { x, y }, isOperate.value);
  });
};

// 调整大屏比例
watch(
  () => screenSize.value,
  (v) => {
    column.value = v;
    screenWidth.value = SCREEN_SIZE[v].width - 64;
    document.value.setScreenProps('size', SCREEN_SIZE[v].size);
    // 先清空再赋值 否则尺寸会有影响
    nodesArr.value = [];
    nextTick(() => {
      gridInit();
    });
  },
  { immediate: true }
);

// 删除节点后更新
watch(
  () => props.nodes,
  (newVal, oldVal) => {
    if (oldVal.length === 0) return;
    nodesArr.value = newVal;
  },
  { immediate: true }
);

// 切换运营中心视角
watch(
  () => isOperate.value,
  () => {
    nodesArr.value = [];
    grid.value.removeAll();
    nextTick(() => {
      gridInit();
    });
  }
);

const formatTooltip = (val) => {
  return `${val}%`;
};

const updateDragging = (val) => {
  isDragging.value = val;
};

const handleSize = (val) => {
  // 最大放大到200%  最小缩小到20%
  if (val && scaleSize.value < 200) {
    scaleSize.value += 10;
    return;
  }
  if (!val && scaleSize.value > 20) {
    scaleSize.value -= 10;
  }
};

const keyCodeDelete = async (item) => {
  const itemNode = item ? item : selection.value;
  // 只能删除视图、条件组件
  const canDeleteType = ['view', 'condition'];
  if (!itemNode || !canDeleteType.includes(itemNode.type)) return;
  await proxy.$confirm(`是否删除组件：${itemNode.type}`, '删除组件', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning',
  });
  document.value.deleteNode(itemNode.id, props.isOperate);
};
</script>


<style lang="scss" scoped>
.editor-frame--renderer {
  position: relative;
  overflow: auto;
}
.grid-stack {
  height: 100%;
  width: calc(100% - 64px);
  margin-left: 24px;
}
// 画布
.layout-view {
  height: 100%;
  background-color: #f5f7ff;
  overflow: auto;
  &:focus-visible {
    outline: none;
  }
  .scale-btn {
    display: flex;
    width: 140px;
    height: 40px;
    position: absolute;
    bottom: 0px;
    right: 380px;
    z-index: 999;
    background: #ffffff;
    box-shadow: 0 4px 8px 0 #99999933;
    text-align: center;

    .prepend-text,
    .append-text {
      width: 40px;
      color: #86909c;
      box-sizing: border-box;
      line-height: 40px;
      font-size: 20px;
      cursor: pointer;
      display: block;
      &:active {
        background-color: #f5f5f5;
      }
    }
    .scale-text {
      width: 60px;
      display: block;
      height: 40px;
      line-height: 40px;
      font-size: 14px;
    }
  }
  .screen-content {
    width: 100%;
    height: 100%;
    position: relative;
    display: flex;
    justify-content: center;
    .screen-decoration {
      position: absolute;
      inset: 0;
    }
    ::v-deep .q-input__inner {
      height: 40px;
      text-align: center;
      font-size: 14px;
      color: #1d2129;
      border: none;
    }
  }
  .screen-content {
    width: 100%;
    height: 100%;
    position: relative;
    display: flex;
    justify-content: center;
    .screen-decoration {
      position: absolute;
      inset: 0;
      .title {
        position: absolute;
        top: 16px;
        z-index: 2;
        width: 100%;
        pointer-events: none;
        text-align: center;
        height: 49px;
        font-weight: 0;
        font-size: 40px;
        color: #ffffff;
        letter-spacing: 13.33px;
        text-shadow: 0 0 20px #6191ff26;
      }
    }
    img {
      height: 100%;
      display: block;
    }
    .screen-bi {
      position: absolute;
      inset: 0;
    }
  }
}
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
.grid-stack-item-content {
  animation: fadeIn 0.4s ease-in-out forwards;
}
</style>
<style scoped>
::-webkit-scrollbar {
  width: 0px; /*对垂直流动条有效*/
  height: 0px; /*对水平流动条有效*/
}

/*定义滚动条的轨道颜色、内阴影及圆角*/
::-webkit-scrollbar-track {
  background-color: #fff;
  border-radius: 3px;
}

/*定义滑块颜色、内阴影及圆角*/
::-webkit-scrollbar-thumb {
  border-radius: 7px;
  background-color: #dcdcdc;
}

/*定义两端按钮的样式*/
::-webkit-scrollbar-button {
  display: none;
}

/*定义右下角汇合处的样式*/
::-webkit-scrollbar-corner {
  background: white;
  display: none;
}
</style>
